<!DOCTYPE HTML>
<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api/#dom-css-registerproperty" />
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script src="./resources/utils.js"></script>
<style>
div {
    --registered-length-1: 10px;
    --registered-length-2: var(--registered-length-1);
    --registered-length-3: var(--length-1);
    --registered-length-4: calc(var(--length-1) + 40px);
    --registered-length-5: var(--invalid, 70px);
    --registered-length-6: calc(var(--registered-length-3)*4);
    --registered-length-7: var(--123px, 6px);

    --length-1: 20px;
    --length-2: var(--registered-length-1);
    --length-3: calc(var(--123px, 6px) + var(--123px));

    --percentage: 10%;
    --registered-length-invalid: var(--percentage);

    --registered-token-stream-1:var(--invalid);
    --registered-token-stream-2:var(--invalid,fallback);
    --token-stream-1:var(--registered-token-stream-1,moo);

    --registered-length-list-1: 1px, var(--registered-length-1), 2px;
    --registered-length-list-2: 1px, var(--length-1), var(--registered-length-1), 2px;
    --registered-length-list-3: var(--registered-length-list-1), var(--registered-length-list-2);
}
</style>
<div id=element></div>
<script>
test(function() {
    CSS.registerProperty({name: '--123px', syntax: '<length>', initialValue: '123px', inherits: false});

    CSS.registerProperty({name: '--registered-length-1', syntax: '<length>', initialValue: '0px', inherits: false});
    CSS.registerProperty({name: '--registered-length-2', syntax: '<length>', initialValue: '0px', inherits: false});
    CSS.registerProperty({name: '--registered-length-3', syntax: '<length>', initialValue: '0px', inherits: false});
    CSS.registerProperty({name: '--registered-length-4', syntax: '<length>', initialValue: '0px', inherits: false});
    CSS.registerProperty({name: '--registered-length-5', syntax: '<length>', initialValue: '0px', inherits: false});
    CSS.registerProperty({name: '--registered-length-6', syntax: '<length>', initialValue: '0px', inherits: false});
    CSS.registerProperty({name: '--registered-length-7', syntax: '<length>', initialValue: '0px', inherits: false});
    CSS.registerProperty({name: '--registered-length-invalid', syntax: '<length>', initialValue: '15px', inherits: false});

    CSS.registerProperty({name: '--registered-token-stream-1', syntax: '*', inherits: false});
    CSS.registerProperty({name: '--registered-token-stream-2', syntax: '*', inherits: false});

    computedStyle = getComputedStyle(element);
    assert_equals(computedStyle.getPropertyValue('--registered-length-1'), '10px');
    assert_equals(computedStyle.getPropertyValue('--registered-length-2'), '10px');
    assert_equals(computedStyle.getPropertyValue('--registered-length-3'), '20px');
    assert_equals(computedStyle.getPropertyValue('--registered-length-4'), '60px');
    assert_equals(computedStyle.getPropertyValue('--registered-length-5'), '70px');
    assert_equals(computedStyle.getPropertyValue('--registered-length-6'), '80px');
    assert_equals(computedStyle.getPropertyValue('--registered-length-7'), '123px');
    assert_equals(computedStyle.getPropertyValue('--length-1'), '20px');
    assert_equals(computedStyle.getPropertyValue('--length-2'), '10px');
    assert_equals(computedStyle.getPropertyValue('--length-3'), 'calc(123px + 123px)');
    assert_equals(computedStyle.getPropertyValue('--registered-length-invalid'), '15px');

    assert_equals(computedStyle.getPropertyValue('--registered-token-stream-1'), '');
    assert_equals(computedStyle.getPropertyValue('--registered-token-stream-2'), 'fallback');
    assert_equals(computedStyle.getPropertyValue('--token-stream-1'), 'moo');
}, "var() references work with registered properties");

test(function(){
    CSS.registerProperty({
        name: '--registered-length-list-1',
        syntax: '<length>#',
        initialValue: '0px',
        inherits: false
    });
    let computedStyle = getComputedStyle(element);
    assert_equals(computedStyle.getPropertyValue('--registered-length-list-1'), '1px, 10px, 2px');
}, 'References to registered var()-properties work in registered lists');

test(function(){
    CSS.registerProperty({
        name: '--registered-length-list-2',
        syntax: '<length>#',
        initialValue: '0px',
        inherits: false
    });
    let computedStyle = getComputedStyle(element);
    assert_equals(computedStyle.getPropertyValue('--registered-length-list-2'), '1px, 20px, 10px, 2px');
}, 'References to mixed registered and unregistered var()-properties work in registered lists');

test(function(){
    CSS.registerProperty({
        name: '--registered-length-list-3',
        syntax: '<length>#',
        initialValue: '0px',
        inherits: false
    });
    let computedStyle = getComputedStyle(element);
    assert_equals(computedStyle.getPropertyValue('--registered-length-list-3'), '1px, 10px, 2px, 1px, 20px, 10px, 2px');
}, 'Registered lists may be concatenated');

test(function(){
    CSS.registerProperty({
        name: '--length-em',
        syntax: '<length>',
        initialValue: '0px',
        inherits: false
    });
    element.style = 'font-size: 11px; --length-em: 10em; --unregistered:var(--length-em);';
    let computedStyle = getComputedStyle(element);
    assert_equals(computedStyle.getPropertyValue('--unregistered'), '110px');
    element.style = '';
}, 'Font-relative units are absolutized when substituting');

test(function(){
    CSS.registerProperty({
        name: '--length-calc',
        syntax: '<length>',
        initialValue: '0px',
        inherits: false
    });
    element.style = 'font-size: 11px; --length-calc: calc(10em + 10px); --unregistered:var(--length-calc);';
    let computedStyle = getComputedStyle(element);
    assert_equals(computedStyle.getPropertyValue('--unregistered'), '120px');
    element.style = '';
}, 'Calc expressions are resolved when substituting');

test(function(){
    CSS.registerProperty({
        name: '--length-calc-list',
        syntax: '<length>#',
        initialValue: '0px',
        inherits: false
    });
    element.style = 'font-size: 11px; --length-calc-list: 10em, calc(10em + 10px); --unregistered:var(--length-calc-list);';
    let computedStyle = getComputedStyle(element);
    assert_equals(computedStyle.getPropertyValue('--unregistered'), '110px, 120px');
    element.style = '';
}, 'Lists with relative units are absolutized when substituting');

test(function(){
    let length = generate_property('none | <length>');
    let universal = generate_property('*');
    element.style = `font-size: 10px; ${length}: 10em; ${universal}: var(${length})`;
    let computedStyle = getComputedStyle(element);
    assert_equals(computedStyle.getPropertyValue(universal), '100px');
    element.style = '';
}, 'Values are absolutized when substituting into properties with universal syntax');

test(function(){
    let name = generate_property('<length>');
    let unregistered = '--unregistered'
    element.style = `${name}: red; ${unregistered}: var(${name})`
    let computedStyle = getComputedStyle(element);
    assert_equals(computedStyle.getPropertyValue(unregistered), '');
}, 'Invalid values for registered properties are serialized as the empty string')

function test_valid_fallback(syntax, value, fallback) {
    test(function(){
        let name = generate_property(syntax);
        try {
            element.style = `${name}: ${value}; --x:var(${name},${fallback})`;
            let computedStyle = getComputedStyle(element);
            assert_equals(computedStyle.getPropertyValue('--x'), value);
        } finally {
            element.style = '';
        }
    }, `Valid fallback does not invalidate var()-reference [${syntax}, ${fallback}]`);
}

function test_invalid_fallback(syntax, value, fallback) {
    test(function(){
        let name = generate_property(syntax);
        try {
            element.style = `${name}: ${value}; --x:var(${name},${fallback})`;
            let computedStyle = getComputedStyle(element);
            assert_equals(computedStyle.getPropertyValue('--x'), value);
        } finally {
            element.style = '';
        }
    }, `Invalid fallback doesn't invalidate var()-reference [${syntax}, ${fallback}]`);
}

test_valid_fallback('<length>', '40px', '10px');
test_valid_fallback('<length> | <color>', '40px', 'red');
test_valid_fallback('<length> | none', '40px', 'none');

test_invalid_fallback('<length>', '40px', 'red');
test_invalid_fallback('<length> | none', '40px', 'nolength');
test_invalid_fallback('<length>', '40px', 'var(--novar)');

test(function(t){
    CSS.registerProperty({
        name: '--registered-universal-no-initial',
        syntax: '*',
        inherits: false
    });
    t.add_cleanup(() => {
        element.style = '';
    });
    element.style = [
        '--registered-universal-no-initial:;',
        'background-color: var(--registered-universal-no-initial) green',
    ].join(';');
    let computedStyle = getComputedStyle(element);
    assert_equals(computedStyle.getPropertyValue('background-color'), 'rgb(0, 128, 0)');
}, 'Empty universal custom property can be substituted with var()');

</script>
